#include "asn1window.h"
#include "ui_asn1window.h"
#include "QFileDialog"
#include <openssl/bio.h>
#include <openssl/asn1.h>
#include "QDebug"
#include "QMessageBox"

ASN1Window::ASN1Window(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::ASN1Window)
{
    ui->setupUi(this);
    ui->IDLabel_value->setMinimumWidth(150);
    ui->LengthLabel_value->setMinimumWidth(150);
    ui->ValueLabel_value->setMinimumWidth(150);
    ui->ValueLabel_value->setMaximumWidth(300);
    ui->ValueLabel_value->setWordWrap(true);
    ui->ValueLabel_value->setAlignment(Qt::AlignBottom);
    ui->IDLabel->setMinimumWidth(110);
    ui->LengthLabel->setMinimumWidth(110);
    ui->ValueLabel->setMinimumWidth(110);
    ui->treeWidget->setHeaderLabel("asn1 tree");
    // 以上初始化组件
    connect(ui->treeWidget, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(on_set_LabelText(QTreeWidgetItem*, int)));
    // 绑定信号和槽。当树形结构的节点被点击，发出信号，唤醒on_set_LabelText事件改变Label的值
}

ASN1Window::~ASN1Window()
{
    delete ui;
}

// 用户点击Open File，触发此事件
void ASN1Window::on_actionOpen_File_O_triggered()
{
    // 获取选择的文件路径，限制后缀名
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
                                                    "/home",
                                                    tr("(*.der *.cer *.crt)"));
    if (!fileName.isEmpty())
    {
        QString parse_result = parseAsn1(fileName);
        if (parse_result.compare("can not parse") == 0)
        {
            QMessageBox *msgBox =
                new QMessageBox("title",
                    "必须是der编码,你可能选择了一个pem编码的文件",
                    QMessageBox::Critical,
                    QMessageBox::Ok | QMessageBox::Default,
                    QMessageBox::Cancel | QMessageBox::Escape,
                    0);
            msgBox->show();
            return;
        }
        if (parse_result.compare("fail to open file") == 0)
        {
            QMessageBox *msgBox =
                new QMessageBox("title",
                    "fail to open file",
                    QMessageBox::Critical,
                    QMessageBox::Ok | QMessageBox::Default,
                    QMessageBox::Cancel | QMessageBox::Escape,
                    0);
            msgBox->show();
            return;
        }
        renderASN1(parse_result);
    }
}

// 解析ASN.1编码文件
QString ASN1Window::parseAsn1(QString filename)
{
    std::string file_name = filename.toStdString();
    const char* fileName = file_name.c_str();
    FILE *fp = fopen(fileName, "rb");
    if (fp == NULL)
    {
        return QString("fail to open file");
    }
    unsigned char buf[5000];
    int len = fread(buf, 1, 5000, fp);
    fclose(fp);
    BIO *bp = BIO_new(BIO_s_mem());
    int ret = ASN1_parse_dump(bp, buf, len, 7, 20);
    if (ret == 0)
    {
        return QString("can not parse");
    }
    len = BIO_ctrl_pending(bp);
    char *out = (char*)OPENSSL_malloc(len);
    len = BIO_read(bp, out, len);
    QString re = QString(out);
    OPENSSL_free(out);
    BIO_free(bp);
    return re;
}

// 将parseAsn1的解析结果渲染到界面上
void ASN1Window::renderASN1(QString asn1)
{
    ui->treeWidget->clear();
    ui->ValueLabel_value->clear();
    ui->IDLabel_value->clear();
    ui->LengthLabel_value->clear();
    QStringList asn1nodes = asn1.split("\n");
    QHash<int, QTreeWidgetItem*> map; // 哈希表存储父节点
    QTreeWidgetItem *p = NULL;
    QRegExp rx("^\\s*\\d+:d=(\\d+)\\s*hl=(\\d+)\\s*l=\\s*(\\d+)\\s*(cons:|prim:)\\s*([A-Za-z0-9\\s\\[\\]]+)\\:?([\\w\\W]*)$");
    // 正则匹配，获得节点的各个属性
    for (int i = 0; i < asn1nodes.size(); i++)
    {
        QString asn1row = asn1nodes.at(i);
        int pos = rx.indexIn(asn1row);
        if (pos == -1)
        {
            QString tmp = p->data(2, 0).toString();
            tmp = tmp + "\n" + asn1row.trimmed();
            p->setData(2, 0, tmp);
            continue;
        }
        QStringList list = rx.capturedTexts();
        int d = list.at(1).toInt();
        QString l = list.at(3);
        QString type = list.at(5).trimmed();
        QString value = list.at(6).trimmed();
        if (d == 0)
        {
            QTreeWidgetItem *first = new QTreeWidgetItem(ui->treeWidget);
            first->setText(0, type);
            first->setData(1, 0, l);
            first->setData(2, 0, value);
            map.insert(0, first);
            p = first;
        }
        else
        {
            QTreeWidgetItem *parent = map.value(d - 1);
            QTreeWidgetItem *item = new QTreeWidgetItem(parent);
            item->setText(0, type);
            item->setData(1, 0, l);
            item->setData(2, 0, value);
            map.insert(d, item);
            p = item;
        }
    }
}

// 当树形节点被点击，触发此事件。item 可以看做指向被点击节点的指针
void ASN1Window::on_set_LabelText(QTreeWidgetItem* item, int)
{
    ui->IDLabel_value->setText(item->text(0));
    ui->LengthLabel_value->setText(item->data(1, 0).toString());
    QString value = item->data(2, 0).toString();
    ui->ValueLabel_value->setText(value);
}



